#define PrynInternal
#define PrynImplementation
#include <pryn.h>

#define PrynCheckType() \
	prynAssert (type, NullArgument); \
	state = type->pState;

#define PrynTypeBody(BODY) PrynBaseBody (type, { state = type->pState; BODY })
#define PrynTypeGetProperty(PropName, PropType, PropInitial, PropValue) PrynBaseGetProperty (, Type, type, PropName, PropType, PropInitial, { *output = PropValue; })
#define PrynTypeSetProperty(PropName, PropType, PropAssign) PrynBaseSetProperty (, Type, type, PropName, PropType, PropAssign)

static PrynResult defaultConnectionImage (PrynValue *value, int width, int height, PrynColor *image, PrynColor *fallback, void *data)
{
	return prynTypeClearConnectionImage (width, height, image, fallback, prynColorBlack);
}

static PrynResult defaultDestroy (PrynValue *value, void *data)
{
	if (!value || !value->pType) return PrynResultNullArgument;
	prynFree (value->pType->pState, value->pPointer);
	return PrynResultSuccess;
}

PrynExport (PrynResult, prynTypeState, (PrynType *type, PrynState **result))
{
	if (result) *result = 0;
	if (!type || !result) return PrynResultNullArgument;
	*result = type->pState;
	return PrynResultSuccess;
}

PrynExport (PrynResult, prynTypeTags, (PrynType *type, PrynTagList **result))
{
	if (result) *result = 0;
	if (!type || !result) return PrynResultNullArgument;
	*result = &type->pCommonObject.pTags;
	return PrynResultSuccess;
}

PrynExport (PrynResult, prynTypeId, (PrynType *type, PrynString *result))
{
	prynStringNull (result);
	if (!type || !result) return PrynResultNullArgument;
	*result = type->pId;
	return PrynResultSuccess;
}

PrynExport (PrynResult, prynTypeName, (PrynType *type, PrynString *result))
{
	prynStringNull (result);
	if (!type) return PrynResultNullArgument;
	if (result) *result = type->pCommonResource.pName;
	return PrynResultSuccess;
}

PrynExport (PrynResult, prynTypeDescription, (PrynType *type, PrynString *result))
{
	prynStringNull (result);
	if (!type) return PrynResultNullArgument;
	if (result) *result = type->pCommonResource.pDescription;
	return PrynResultSuccess;
}

PrynExport (PrynResult, prynTypeSetName, (PrynType *type, const PrynString *value)) { PrynTypeBody ({ prynCommonResourceSetName (&type->pCommonResource, value); }); }
PrynExport (PrynResult, prynTypeSetDescription, (PrynType *type, const PrynString *value)) { PrynTypeBody ({ prynCommonResourceSetDescription (&type->pCommonResource, value); }); }

PrynSetCallbackDefinition (, Type, CreateFunction, self->pState, 0)
PrynSetCallbackDefinition (, Type, DestroyFunction, self->pState, &defaultDestroy)
PrynSetCallbackDefinition (, Type, ConnectionImageFunction, self->pState, &defaultConnectionImage)
PrynSetCallbackDefinition (, Type, ColorsFunction, self->pState, 0)

PrynExport (PrynResult, prynTypeFind, (PrynType **output, PrynState *state, const PrynString *id))
{
	PrynStartFunctionHasState ();
	PrynType *type = 0;
	
	if (output) *output = 0;
	prynAssert (state && output && id, NullArgument);

	for (type = state->pTypes.first; type; type = type->pNext)
		if (!prynStringEqual (&type->pId, id))
		{
			*output = type;
			prynReturnSuccess ();
		}
		
	prynCall (prynAllocateClearedObject (state, &type));

	type->pCommonObject.pType = PrynObjectTypeType;
	type->pId = *id;
	type->pState = state;
	type->pDestroyFunction = &defaultDestroy;
	type->pConnectionImageFunction = &defaultConnectionImage;
	type->pCommonObject.pMark = true;
	prynDListAppendP (state->pTypes, type);
	*output = type;

	PrynEndFunction ();
}

PrynExport (PrynResult, prynTypeCreate, (PrynType **output, PrynLibrary *library, const PrynString *id))
{
	PrynStartFunction ();
	PrynType *type;
	
	if (output) *output = 0;
	prynAssert (output && library, NullArgument);
	state = library->pState;
	prynAssert (id && id->pointer, NullArgument);
	
	prynCall (prynTypeFind (&type, state, id));
	type->pLibrary = library;
	*output = type;
		
	PrynEndFunction ();
}

PrynExport (PrynResult, prynTypeClearConnectionImage, (int width, int height, PrynColor *image, PrynColor *fallback, PrynColor value))
{
	PrynColor *end; // Pointer after the end of the image, used to halt iteration.

	if (fallback)
		*fallback = value;
	if (image) for (end = image + width * height; image < end; image ++)
		*image = value;
		
	return PrynResultSuccess;
}

PrynExport (PrynResult, prynTypeDestroy, (PrynType *type))
{
	PrynStartFunction ();

	prynAssert (type, NullArgument);
	state = type->pState;

	while (type->pValues.first)
		prynValueDisassociate (type->pValues.first);

	prynCommonObjectDestroy (&type->pCommonObject, state);

	PrynDestroyCallback (type->pCreateFunction);
	PrynDestroyCallback (type->pDestroyFunction);
	PrynDestroyCallback (type->pConnectionImageFunction);
	PrynDestroyCallback (type->pColorsFunction);

	prynCommonResourceDestroy (&type->pCommonResource, state);
	prynDListRemoveP (state->pTypes, type);
	prynFree (state, type);

	PrynEndFunction ();
}